Common Lisp Notes
Table of Contents
Resources
Source Files
- Common Lisp source files can have the
.lisp
extension
Multi-Paradigm
- ANSI Common Lisp is multi paradigm
- It supports functional, generic, object oriented and domain specific programming styles.
SLIME
- Common Lisp development environment for Emacs
- The official webpage is, https://common-lisp.net/project/slime/
- To use with org babel ensure that
lisp
is enabled in yourorg-babel-load-langauges
- Start slime
M-x slime
before attempting to evaluate a common lisp source block withC-c C-c
- You should be able to access variables and functions inside the SLIME REPL that are defined in the source code blocks
- Use
C-c C-d d
to view the documentation for the symbol at point- This might show you a link to http://www.ai.mit.edu to get the real documentation
CLISP
- Easy to use and portable common lisp compiler that runs on most operating systems
- Also provides debugger and interpreter
SBCL
- Steel Bank Common Lisp descends from Carnegie Mellon, Steel being Andrew Carnegie's industry and Banking being Andrew Mellon's industry
- Considered more heavy duty than CLISP
- Also provides debugger and interpreter
SBCL Specific
Create a binary executable from a lisp image
- Ensure that you are running sbcl without slime/swank (the multiple threads from that will break the binary export)
- To create the binary use the following template:
(sb-ext:save-lisp-and-die "my-binary" :toplevel #'main :executable t)
- This should create a binary that is the entire lisp image
Common Lisp Syntax
Anonymous (Lambda) Functions
- This creates a lambda function that doubles the input
(lambda (x) (* 2 x))
Cons Pairs (Dotted Lists) vs Lists
- A list is really a chain of cons that ends with an empty list or nil
- For exmaple
'(1 2 3)
isequal
to(cons 1 (cons 2 (cons 3 nil))
- Any cons cell that doesn't end in nil is known as a dotted list
(cons 1 2)
is displayed by the REPL as(1 . 2)
- You can create dotted lists with a quote as well,
'(1 . 2)
- You can make a list using quote and dot,
'(1 . (2 . (3 . nil)))
is equal to'(1 2 3)
(cdr '(1 . 2))
is2
not(2)
(cdr '(1 2))
is(2)
not2
- Cons pairs are useful for x/y coordinates and key value pairs
Circular Lists
- When using circular lists you should set the global
*print-circle*
to true,(setf **print-circle** t)
- The above enables complex printing features to be enabled when using self-referential lists
- Below is an example of creating a circular list
(defparameter x '(1 2 3)) (setf (cdddr x) x)
- You can use infinitely large index values in the above example
- For instance
(nth 1000 x)
would return2
Literals
Char
- You can represent a character literal with a
#
followed by a backward slash and the character - For example
#\a
is the representation of the charactera
Newline
#\newline
Tab
#\tab
Space
#\space
nil
- nil can be represented with
nil
,'nil
,()
, or'()
nil
is the only Lisp object that is both a symbol and a list
Hex Numbers
#xFF
Symbols
- Symbols in Common Lisp are case insensitive
- It is common to only use lowercase when writing common lisp source code
- To make a symbol that is case sensitive surround it with pipe,
|
, characters- This also allows you to use punctuation in a symbol name
|Ca$e mATT3rz|
is an example
- There are a variety of functions that can look up specific properties of a symbols
- They all start with the
symbol-
prefix - For example
symbol-value
will show the value associated with a symbol symbol-function
will return the reference to the function a symbol points to- Be sure to quote the symbol name when using these functions, i.e.
(symbol-value 'my-symbol)
- Symbols are really just blocks of pointers that point to values, and functions
- Because a symbol can have a value and function that allows functions and variables to not have name collisions
Quoting
- Use the single quote
'
to quote a list - To quasiquote use the backtick
`
and the comma to unquote,
`(one plus two is ,(+ 1 2))
- The
#
before the quote is used to indicate the quoted symbol is a function - Common Lisp is a LISP-2 rather than a LISP-1 like Scheme
- This means it has 2 separate scope for functions and data
- You can have a function and data with the same symbol name
- The caveat is you must use the
#
when quoting a function
- When quasiquoting use a splicing command
,@
lists are spread when the quoted code is expanded
Variables
Globals
- Use
defparameter
to create global variables- The
defparameter
function will mutate the value of a global if it already exists
- The
- It is a common practice to surround global variables with earmuffs (asterisks)
- For example a global named num would look like this
*num*
- Another option to create globals is
defvar
- If the variable already exists
defvar
will not change the existing value
- If the variable already exists
Local variables
- Use
let
function to define local variables, you cannot reference other variables in the list - The
let*
function is the same aslet
but it lets you reference preceding variable names
(let* ((x 10)
(y (* 2 x)))
(+ x y))
Mutating
- Use
setf
to change the value of an existing variable
Functions
Globals
- Use
defun
to define a global function
(defun function_name (arguments) ...body)
Local Named Functions
- To define a local function use the
flet
form flet
is very similar tolet
for variables but instead
(flet ((function_name (arguments)
...function body))
...body)
- If you want to reference other local functions in an
flet
you should uselabels
instead - You can also
labels
to call a local named function recursively
(labels ((function_a (n)
(+ n 2))
(function_b (n)
(function_a (function_a n))))
(function_b 2))
Argument Limits
- There is a limit on the number of arguments a function can have
- You can check those limits in the Common Lisp REPL with
call-arguments-limit
Optional Arguments
- Adding
&optional
before an argument marks it as optional - This means that when the function is evaluated if you do not supply that argument it will not error
(defun fn-opt (not-optional &optional (val 10)) (list not-optional val)) (fn-opt 100)
- The above example shows an optional argument
val
with a default value of10
- It returns
(100 10)
Variadic Arguments
- Using
&rest
followed by a variable name collects a variable amount of arguments into a list
(defun fn-var (x &rest args) (append (if (listp x) x (list x)) args)) (fn-var (fn-var 10) 100 10 10)
- The above example returns the list
(10 100 10 10)
Keyword Arguments
- Using
&key
all of the arguments appearing after it will be named - To use a named argument it must be formatted with
:<NAME_OF_ARG
- The preceding colon is a keyword form
(defun fn-key (&key x y w h) (list x y w h)) (fn-key :w 20 :h 10 :x 5 :y 2)
- The above example returns the list
(5 2 20 10)
- It shows how when the arguments are used as keywords the order does not matter
Body Argument
- The
&body
is the same as&rest
but it allows editors to indent differently for the remainder since it is abody
&body
can only be used with macros
Default Values
- Optional and keyword arguments can have a default value
- To specify the default value you create a list with that starts with the local name of the argument followed by the default value
- Additionally you can supply a third item to the list that is a boolean indicating if the value was set or not
(defun fn-default (&optional (val 12 val-set)) (list val val-set)) (fn-default 100)
- The above example returns the list
(100 t)
Closures
- Referencing variables in a lambda expression can prevent values from being garbage collected
- This can be useful for memoizing functions
(let ((count 0)) (defun say-hello-count () (incf count) (format nil "Hello, for the ~a time" count))) (say-hello-count) (say-hello-count)
- The above example shows using lexical binding to capture a closure variable inside the function
- The count variable is incremented each time you call
say-hello-count
- The
defun
macro will allow you to callsay-hello-count
outside of thelet
Get the function bound to a symbol
- Use the
symbol-function
function to get the function bound to a symbol - This is useful if you want to create a higher order function that wraps an existing function and you need to store the original function
Tail Call Optimization
- Tail call optimization is a way to get additional performance with recursive functions
- Normally when a function has a recursive call it will add to the stack
- If the function has too many recursive calls it can lead to a stack overflow
- If the recursive call is the final statement in the function the lisp compiler can recognize this as a candidate for tail call optimization
- The tail call optimization is the compiler not adding to the stack since you are already at the correct location
- Not all lisp compilers support this since it is not part of the standard, unlike Scheme which requires tail call optimization
- In clisp you need to explicitly
compile
the function to get the tail call optimization
Nullary Functions (Thunks)
- A nullary function is a function that has no arguments
- They are also commonly known as thunks or suspensions
Returning multiple values
- It is possible to write a function that returns multiple values
- When returning multiple values the first value returned is given preference when chaining the function
- This behavior could be achieved with a list but if it is a special case when you need more than one value this could be cleaner
values
- Use the
values
function to return multiple values from a function
(defun multi-v () (values 'foo 'bar)) (multi-v)
- The above example will return
'foo; 'bar
multiple-value-bind
- To access all of the values returned by a function use
multiple-value-bind
- This allows you to name the values returned by the function and access them in a
let
style block
(multiple-value-bind (x y) ((lambda () (values 'foo 'bar))) (list x y))
- The above example will return the list
(foo bar)
Eval
- You can evaluate a quoted symbol with
eval
(eval '(+ 2 2))
should return4
- Like
eval
in JavaScript it can be a huge security risk in your program
Loops
loop
- Loops can be a good alternative to recursion, especially when using an implementation that does not have tail recursion support
- The most basic form of the
loop
is shown below
(loop (sexp) (sexp) ... (when (predicate) (return)))
- The
return
command exits the loop - Loop can be used to create lists
- Loop has a few keywords that tell it how to behave
- The
collect
keyword specifies what you want to put into the returned list for this iteration - The
repeat
keyword indicates how many times a loop should run
(loop repeat 10
collect 8)
- The
for
andfrom
/to
keywords let you specify a variable local to the loop that increments each loop iteraction - This is inclusive and on both ends
(loop for n from 1 to 10
collect n)
- The above example will return
(1 2 3 4 5 6 7 8 9 10)
- The
for
andbelow
keywords start from 0 and iterate through every integer less than the value afterbelow
(loop for i below 10
collect)
- The above example will return
(0 1 2 3 4 5 6 7 8 9)
- There are many special tokens that can be passed to the
loop
macro that do special things
(loop for y below 10 collect (loop for x below 10 collect (cons x y)))
- The above example generates all of the 2d points between 0 - 10
- Each row is a list of cons cells of points for a given y value
(defparameter *test* '(1 2 3)) (loop for i below (length *test*) do (princ (format nil "~d = ~d~%" i (nth i *test*))))
- The above example returns the following
0 = 1 1 = 2 2 = 3
dotimes
dotimes
takes a variable and an upper bound- It will perform the actions inside the body up to the upper bound
(dotimes (i 4) (princ (format nil "Loop #~d~%" i)))
- The above example will print the following output
Loop #0 Loop #1 Loop #2 Loop #3
Macros
- Use
defmacro
to define a lisp macro - Macro definition is similar to function definition
- A macro usually return a quoted or quasi-quoted lisp expression that is expanded during compile time
- In order to view an expanded use the
macroexpand
function - It is possible to define a macro that adds lexical variables, macros that do this are called anaphoric macros
Common Lisp Functions
Math
Incrementing
- You can use
1-
to decrement by 1,(1- 10)
evaluates to 9 - You can also use
1+
to increment by 1,(1+ 9)
evaluates to 10 - Additionally there are
incf
anddecf
to increment or decrement a value in place - Like
setf
theincf
anddecf
functions must be called with a variable as a parameter
(defparameter n 0) (incf n) (incf n 2) (decf n 3)
- In the above example
n
starts at 0, then goes to1
, then to2
and finally back to0
- The
incf
anddecf
functions also return the new value in addition to mutating the variable
Exponent
- Use
expt
to raise a number to an exponent,(expt 53 53)
raises 53 to the 53rd power
Random numbers
- Use the
random
function to generate a random number - It takes the limit as an argument which can be either an int or a float
- The returned number will be between 0 and the limit
- If it is a float than the random number generated will also be a float
- This has the side effect of changing the internal
random-state
Arithmetic Shift (bitwise shift)
- https://en.wikipedia.org/wiki/Arithmetic_shift
- Use the
ash
function which takes two arguments, the number and the amount of bits to shift left - To shift right use a negative number
Round
- The
round
function will take a number and return the two values, the rounded integer and the remainder - For example,
(round 16.8)
returns17; -0.2
- When rounding up the remainder will be negative
Modulus (Remainder)
- Use the
mod
function to get the remainder between two numbers
(mod 5 2)
- The above example returns 1
Floor / Integer Truncation
- To get the integer truncation between two numbers use the
floor
function
(floor 5 2)
- The above example returns 2
Largest and Smallest Integers
- The Common Lisp standard defines variables for the largest and smallest integers
most-positive-fixnum
is the largest integermost-negative-fixnum
is the smallest integer
Using /
to get the reciprocal
- When you use the
/
function with only a single value it will return the reciprocal - For example
(/ 4)
returns1/4
Get the value of a bit in a number
- To get the value of an individual bit you can use
ldb
and thebyte
function - For example
(ldb (byte 1 3) 8)
selects the 4th bit from the integer 8 which is 1
Strings
Concatenate
- Use
concatenate
to join multiple strings together - Use the symbol
'string
as the first argument to the function (concatenate 'string "abc" "def")
should return"abcdef"
Converting to and from character lists
- Use
coerce
with either thelist
orstring
type (coerce "ABC" 'list)
should return(#\A #\B #\C)
(coerce '(#\A #\B #\C) 'string)
should return"ABC"
- This can be used with
mapcar
to iterate over each character
(mapcar #'(lambda (c) (princ (format nil "char: ~C~C" c #\newline))) (coerce "abc123" 'list))
Converting symbols to strings
- Use
prin1-to-string
to convert symbols to strings (prin1-to-string 'abc)
should return "ABC"- The
write-to-string
function also can convert data to strings
Multi Line Strings
- Lisp will store the newline character in the string
- So the
\n
character is not needed for multi line strings
(setq mystring "the first line the second line and the third")
- To insert a newline into a string use the
format
function and the~C
control character - The
~C
control chacter means insert a character literal, you can use the#\newline
character literal to get a new line
(format nil "hello~Cworld" #\newline)
- Another way to do this is with
~%
(format nil "hello~%world")
Parsing an Integer from a String
- The
parse-integer
function can be used to parse an integer from a string - The
:radix
keyword lets you specify the base of the number - The
:junk-allowed
keyword lets you indicate you want to just return nil if there is junk in the string
(parse-integer "beef" :radix 16 :junk-allowed t)
- The above example returns the integer 48879
Getting the Integer Code of an Char
- The
char-code
function will return the integer ASCII code for the char - The
code-char
will give you the char for an integer ASCII code of a char
(code-char (char-code #\A))
- The above example will return the character
A
#\A
Turning a String into a Lisp Symbol
- The
intern
function can turn a string into a Lisp symbol
(intern "abc")
- The above example returns the symbol
abc
- There is some overlap between
read
andintern
, in general if theintern
functions does what you need then it is preferable since it is safer than theread
function
Getting the Position of Char in String
- The
position
function can be used to get the index of a char in a string, since a string is a sequence of characters - See Index of item in list
Creating a Fixed Size String
- The
make-string
function will create a string with a given length
(make-string 10 :initial-element #\x)
- The above example creates a string of length 10 filled with the char
x
Changing capitalization of strings
(string-upcase "abc") (string-downcase "ABC")
- The
string-upcase
andstring-downcase
functions will change all letters in a string to desired capitalization
(string-capitalize "hello world")
- The
string-capitalize
function wil capitalize the first letter of each word, the above example produces "Hello World"
Format
- Wikipedia: Format (Common Lisp)
- The
format
function takes 3 parameters, destination, control string and variadic values - The destination
nil
,t
or some stream - When the destination is
nil
theformat
function returns the formatted string - When it is not nil it will return nil and send the output to either
stdout
witht
or some other stream specified
Format Directives
c
- single characterr
- radix based
- decimal (base 10) numberb
- binary (base 2) numbero
- octal (base 8) numberx
- hexadecimal (base 16) numberf
- floating point numbere
- exponent notation for numberg
- exponent or float, pickign automatically$
- print with monetary conventionsa
- print in human friendly manners
- print symbol in format compatible with read functionw
- print with printer control charactersi
- indent a logical blockt
- move cursor to columnp
- prints singular or plural suffix%
- newline unconditionally, similar toterpi
&
- newline conditionally, similar tofresh-line
v
- iteration quantity, use this when you don't want to put the number in the format string
Examples
- Strings
(format nil "Your message is: ~a~%" "Hello, World")
- Left pad zeroes
(format nil "You number is: ~3,'0d," 12)
- The above example will ensure that the decimal value is 3 digits long and will pad to the left with
0
if it is less than 3 digits
- The above example will ensure that the decimal value is 3 digits long and will pad to the left with
- Looping with Format
- You can loop through lists with format using
~{
and~}
(format nil "~{The list has: ~a~%~}" '("abc" "def" "ghi"))
- You can loop through lists with format using
- Create a string of n length with repeated strings
(format t "~v@{~a~:*~}" 20 "*")
- The above example prints a string of 20 asterisks to standard output
- The
~v
tells format to get the quantity from the argument list - The
~@{
starts the loop but the@
means that the remaining arguments will be treated as a list, rather than expecting a list - The
~a
prints the most appropriate string for the data element following the quantity, in this case*
- The
~:*
instructs format to go back one element in the argument list- This is needed since we don't have 20 different elements in the argument list
- Going back after printing one causes it to continue to print the same string over and over
- The
~}
closes out the loop block
(format t "~v@{~a~a~2:*~}" 20 "*" "_")
- The modified above example will repeat
*_
20 times - The only change is that inside the loop we consume 2 items
- Because we consume 2 we need to move back 2 and we do that with
~2:*
- The
:
tells the*
directive to move backwards - The quantity
2
before that tells it to move back that many items
Lists
push
- Adds item to the beginning of a list
- The list must be a variable
(defparameter *some-list* nil) (push 4 *some-list*) (push 3 *some-list*) (push 2 *some-list*) (push 1 *some-list*) *some-list*
last
- You can get the last element of a list with
last
Using push to append
- Since a list is just a
cons
pairs, creating a newcons
pair with the last element will append - The
cdr
of the last element of a list is an empty list ornil
- If you
push
into that empty list you will append to the list - For instance if
a
is(1 2 3)
this should append 4 to the list(push 4 (cdr (last a)))
pushnew
- The
pushnew
function will only add an item to a list if it is not already in it
(defparameter *some-list* '(1 2 3)) (pushnew 1 *some-list*)
- In the above example 1 is already in the list so the results of
pushnew
is the same list(1 2 3)
member
- Checks to see if an item is inside a list
(member 1 '(1 2 3 4))
- This will return true when you check if
nil
is in the list
find
- Use find to search through a list for the first item that matches
- The search value is the first argument
- The second argument is the list that is being searched
- The keyword parameter passed with
:key
tells find how to determine if the list item matches the search (find 20 '((a 5) (b 20) (c 6) (d 20)) :key #'cadr)
should return(b 20)
find-if
- Returns the first item in a list that satisifies the predicate
(find-if #'oddp '(2 4 5 6))
- Returns nil if the item is not found
- This will not work when searching for
nil
in a list
mapcar
- Use
mapcar
to run a function on each element of a list (mapcar (lambda (n) (1+ n)) '(1 2 3))
should return(2 3 4)
- You can also run
mapcar
over multiple sequences (mapcar (lambda (m n) (list m n)) '(1 2 3) '(a b c))
should return((1 a) (2 b) (3 c))
mapc
is a more efficient version of mapcar that does not return the listmaplist
is another variant ofmapcar
that gives the remainder of the list as an argument to the function rather than a single item
mapcan
- Similar to
mapcar
but allows the lambda to return a list - The list values are all appended together in the result
(mapcan (lambda (v) (case v (a '(1 2 3)) (b '(4 5 6 7)) (c '(8 9 10 11 12)))) '(a c b))
- The above example will return
(1 2 3 8 9 10 11 12 4 5 6 7)
apply
- Use
apply
to call a functions once with all the elements of a list as its arguments
(defparameter *rect* '(40 40 20 30)) (defun rect-area (x y w h) (* w h)) (apply #'rect-area *rect*)
remove-if-not
- Removes all items from the list that do not satisfy the predicate
(remove-if-not #'oddp '(1 2 3 4 5))
should return(1 3 5)
nth
- Use
nth
to get the value at index n from a list (nth 2 '(7 8 9))
should return9
use setf and nth to change list item value
- You can use
setf
to mutate a list - For example if you have a list named
l
with the value(1 1 1)
(setf (nth 2 l) 4)
should mutatel
to be(1 1 4)
subseq
- Use
subseq
to get a sub sequence of a list - The start index is required and you can optionally add the end index
- The start index is inclusive and the end index is exclusive,
(start end]
(subseq '(9 8 7 6) 1 3)
should return(8 7)
Swapping list items with rotatef
- If you have the list
x
with the value(1 2 3)
- You can swap the 1 and with like so
(rotatef (nth 0 x) (nth 2 x))
- This should return
(3 2 1)
- This should return
- This will mutate the list
concatenate
- Use
concatenate
to join multiple lists together - Use the symbol
'list
as the first argument to the function (concatenate 'list '(1 2 3) '(4 5 6))
should return(1 2 3 4 5 6)
Slicing an item out of a list
- You can generate a new list with a particular index sliced out using
concatenate
andsubseq
- If you have the list
d
with the value(1 2 3 4 5)
(concatenate 'list (subseq d 0 2) (subseq d 3))
should return(1 2 4 5)
Testing the values of a list with every
some
notevery
notany
- These functions run a predicate and return a different boolean value based on their rules
every
returns nil at the first instance of anil
value, similar to logical andsome
returns true if any of the values return truenotany
returns nil if any of the values return truenotevery
returns true if all the values are false(every #'identity '(t t nil)
should return nil(every #'identity '(t t t))
should return true
substitute-if
- Replaces every item in a sequence with the first argument if it passes the second argument predicate
- For instance,
(substitute-if 0 #'oddp '(1 2 3 4 5))
will return(0 2 0 4 0)
Index of item in list
- The index of the first instance of an item in a list can be found with the
position
function
(position 2 '(1 2 3 4 2))
- The above code will return
1
since that is the index of the first 2 in the list
Difference between lists
- To get a list of the items that are difference between a list use the
set-difference
function - This function gives you the items that are in the first list that are not in the second list
(set-difference '(1 2 3) '(a 2 c))
should return the set(3 1)
, the order in the set does not necessarily match the order in the first list(set-difference '(a 2 c) '(1 2 3))
should return the set(c a)
Intersection between lists
- To get the intersection of two lists use the
intersection
function (intersection '(1 2 3) '(a 2 c))
should return(2)
Remove Duplicates
- The
remove-duplicates
function will return a list where no item repeats - You can give it a custom
:test
function to use when comparing items (remove-duplicates '(1 1 2 2 3 3))
should return(1 2 3)
Appling a predicate to a list with some
- The
some
function will run a predicate on each item in a list in order - The first time the predicate returns true the it stops checking the list
(some #'oddp '(2 2 4 8 6))
- The above example will return
nil
since no members of the list are odd
(some #'oddp '(2 2 4 5 6))
- The above example will return
t
since there is one odd member
reduce
- Iterate through a sequence and reduce it down to a single value
- The
reduce
function takes two arguments, a function that reduces two items to 1 and a sequence of items
(reduce #'* '(1 2 3 4 5 6 7))
- The above example will return 5040, which is 1 * 2 * 3 * 4 * 5 * 6 * 7
- In the reducer function the first argument is the accumulated value and the second is the current item in the list
- It is possible to set an initial value for the accumulated value with the
:initial-value
key
(reduce (lambda (acc i) (if (oddp i) (+ acc (* i 2)) acc)) '(2 4 5 1) :initial-value 100)
- The above example results in 112, since the initial value is 100 and the only two odd values in the list are 5 and 1 which * 2 are 12
- The reduce function is generic and can be used on all sequence types (arrays, lists, strings)
sort
- The sort function allows you to arbitrarily sort a list
(sort '(1 2 3 4 5 6) #'>)
- The above example sorts the list in descending order
Association Lists (alists)
- Use
assoc
to find the value of a key in an alist
(assoc 'mykey '((somekey (some-value)) (mykey (my-value)) (otherkey (other-value))))
- alists can have multiple instances of a key inside them
- When this happens
assoc
will return the first instance - If you push new keys into the alist you can overwrite the value of a key while preserving the previous value
- You can use
setf
to change the value of an list,(setf (cadr (assoc '2 alist)) t)
- alists are not very efficient beyond a dozen items
- alists can also be implemented using cons pairs
(assoc 'mykey '((somekey . somevalue) (mykey . myvalue) (otherkey . othervalue)))
Logic / Conditionals
Complementing Predicates
- If you have a predicate and you want the opposite of it or complement you can use the higher order function
complement
to achieve that - For example
(substitute-if 0 (complement #'oddp) '(1 2 3 4 5))
should return(1 0 3 0 5)
Shortcut Boolean Evaluation
- When evaluating an
or
orand
boolean operator lisp will stop when it encounters the first symbol that evaluates to either true or false - For example when evaluating an
or
the first true that is encountered causes lisp to stop - When evaluating an
and
the first false encounted causes lisp to stop - This allows you to build conditionals out of
or
orand
statements
- and
(defun pred_a () t) (defun pred_b () t) (defun work () 'work-when) (when (pred_a) (when (pred_b) (work)))
(defun pred_a () t) (defun pred_b () t) (defun work () 'work-and) (and (pred_a) (pred_b) (work))
- or
(defun pred_a () nil) (defun pred_b () nil) (defun work () 'work-unless) (unless (pred_a) (unless (pred_b) (work)))
(defun pred_a () nil) (defun pred_b () nil) (defun work () 'work-or) (or (pred_a) (pred_b) (work))
Using case
to switch through multiple options
- The
case
function can be used to handle multiple conditionals for a single value - A conditional named
otherwise
(defvar x 1234) (case x (123 (princ "x is 123")) (456 (princ "x is 456")) (otherwise (princ "beats me")))
- The above example prints "beats me" since
x
does not match any of the conditionals
Equality
- Use
eq
for comparing symbolseq
returns true when two symbols point to the samecons
eql
will also return true when characters and numbers are used instead of symbols
- Use
equal
for comparing everything elseequal
will tell you if two things are isomorphic (look the same)equalp
will return true when strings have different capitalization, or numbers are not the same type (floats vs ints)
- The
=
comparison is meant primarily for numbers string-equal
is specific for stringschar-equal
is specific for chars
I/O
Printing and Reading
- Use
print
to display a string on stdout- This will automatically add a new line at the end of the string
prin1
andprinc
will not add thenewline
print
will print values as they are stored in Lisp, so strings will have quotes and literals are displayed as such(print #\newline)
will actually print#\newline
to stdout
- Use
princ
to not add the quotation marks and use the characters the literals represent(princ #\newline)
will just print an empty line
- The goal of
print
is to output data in a way that it could be re-read back into its internal representation
- Use
read
to read from stdin- This function is called with no arguments and returns after the user has typed something and pressed enter
- You can use this to assign the value into a variable:
(let ((user-input (read))))
- Both
print
andread
can handle any Lisp data type, including symbols - Use
read-line
to read the input as a string only rather than any valid Lisp data - Use the function
fresh-line
to print a new line if the cursor is not at the beginning of a line:(fresh-line)
- The
terpri
function will always print a new line even if the cursor is at the beginning of the line - The
read-sequence
function will fill a sequence with items from a stream
Files
with-open-file
optionally accepts a steam and file name to open a file- If you do not pass in an existing stream a new one is created
- With the stream variable print functions can send their output to that file
- If the stream is
*standard-output*
then the print functions will automatically send the output to the file
(with-open-file (stream "~/tmp/testfile.txt" :direction :output :if-exists :supersede) (princ "Hello World!" stream) (princ #\newline stream))
- The below example shows routing standard output to a file and appending to the file
- The
finish-output
function will empty the buffers into the file
(with-open-file (*standard-output* "~/tmp/test.log" :direction :output :if-does-not-exist :create :if-exists :append) (loop repeat 100 for x from 0 do (progn (fresh-line) (format t "loop #~d" x) (finish-output) (sleep 1))))
- There is a global stream
*standard-ouput*
that represents stdout of the lisp environment
Arrays
- Any one dimensional array is also known as a vector
make-array
- The
make-array
function is used to create an array - It takes an argument that specifies the size of the array
- It will return an array that size with each element initialized to
nil
aref
- To access a member of an array use
aref
- For example, say you have
some-list
that has this value,#(1 2 3)
to access array index 1 witharef
you would do this,(aref some-list 1)
, this will return 2 aref
can be combined withsetf
to mutate an array(setf (aref some-list 1) 5)
will mutatesome-list
into#(1 5 3)
Hash Tables
make-hash-table
- The
make-hash-table
function will create an empty hash table
gethash
- The
gethash
function is used to access items from a hash table - The first argument of the
gethash
function is the key, the second is the hash table (gethash 'foo some-hash-table)
will access thefoo
key in the hash tablesome-hash-table
- This can also be used with
setf
to set values in the hash table,(setf (gethash 'foo some-hash-table) 'bar)
- The
gethash
function returns multiple values, the first is the value stored in the hash-table for the key, the second is whether or not that key was in the table - This is needed since a key could be present in the table but have
nil
set as its value
Key Equality
- By default hash tables use
eq
to compare equality - This will cause issues when you want to use a symbol as a key
- The equality function in a hash table can be changed using the
:test
key inmake-hash-table
(make-hash-table :test #'equal)
lets you define a table whose keys are compared with theequal
function instead ofeq
- This is useful when you want to have keys are cons pairs
Removing entries with remhash
- The
remhash
function mirrors thegethash
function but it removes the key from the table
(defparameter my-hash (make-hash-table)) (setf (gethash 'foo my-hash) 'bar) (remhash 'foo my-hash)
- The above example adds and removes a key from the
my-hash
table
Profiling
time
- The
time
function will perform the argument function and return a lot of use profile information
Real time: 0.73878 sec. Run time: 0.738236 sec. Space: 31004976 Bytes GC: 26, GC time: 0.098185 sec.
- The above is an example of some of the profile data that the
time
function will return
Generics
setf
- In general the code for getting data out of something is the same as code for putting something in
- The
setf
command is a generic setter that can put data into data structures using the accessor as an argument - The following
setf
example changes the third item of the listfoo
,(setf (third foo) 'bar)
- The first parameter in
setf
is a generalized reference - A generalized reference parameter can be arbitrarily complicated, meaning whatever path needed to access the reference you will still be able to mutate it with
setf
Type Predicates
- Common Lisp has dynamic typing, so a symbol can be any type
- This is a list of all the type predicates:
arrayp
characterp
consp
functionp
hash-table-p
listp
stringp
symbolp
atom
(this is the opposite ofconsp
, anything that is not a cons cell matches)null
Only returns true if input isnil
, same as thenot
predicate
- These predicates an be used to implement generic functions
defmethod
- The
defmethod
macro will allow you to define separate functions for each supported argument type - This can help with code readability so you don't have have a long conditional that checks the type of the arguments
- When using
defmethod
you need to explicitly state the type of each argument
(defmethod add ((x number) (y number)) (+ x y)) (defmethod add ((x string) (y string)) (format nil "string add:~%~4tx: ~a~%~4ty: ~a~%" x y)) (add (format nil "num add: ~d" (add 100 -100)) (format nil "num add: ~d" (add 2 2)))
- The above example will produce the following ouput
string add: x: num add: 0 y: num add: 4
type-of
- The
type-of
function will return the type of the variable - For example,
(type-of 12)
will returninteger
(type-of 'hello)
will returnsymbol
Exceptions / Errors
- To generate an exception or error use the
error
function
(error "this will generate an error")
- The above example shows generating a simple error with a custom error message
Custom Error Conditions
- The function
define-condition
allows you to define a custom error condition
(define-condition my-error-condition () () (:report (lambda (condition stream) (princ "custom report message for `my-error-condition'" stream)))) (error 'my-error-condition)
- The above example creates a custom error condition with a customized report
- Use the
error
function and the symbol of the condition to trigger it
Handling Errors
- The
handler-case
function allows you to intercept an error
(define-condition my-error-condition () () (:report (lambda (condition stream) (princ "custom report message for `my-error-condition'" stream)))) (defun going-to-error () (error 'my-error-condition)) (handler-case (going-to-error) (my-error-condition () "Intercepted `my-error-condition'"))
- The above example shows using
handler-case
to intercept a particular error symbol
Generate Random Symbols
- Sometimes its useful to generate a random symbol
- This can be handy for macros that need to lexically bind a value but don't want to accidentally have variables shadowed by lisp forms supplied by the user
- The
gensym
function will return a random unique symbol
Sleeping
- Common Lisp has a
sleep
function that takes the number of seconds you want to sleep as an argument
Common Lisp Data Structures
List
- Most fundamental data structure in a lisp
- A series of nested
cons
pairs terminated with nil - The access time for elements inside lists is not constant
Array
- Arrays are like like lists except the access time for any element is constant
- An array literal is preceded with a
#
to distinguish it from a list, for example,#(1 2 3)
- Arrays are in general faster than lists when accessing or setting specific elements
Hash Table
- A hash table is very similar to an alist
- A hash table literal is preceded with a
#S
to distinguish it from a list and an array - Similar to arrays hash tables have a constant look up time
- Hash tables are less efficient than alists for really small tables
- Very large hash tables can be paged out to virtual memory which could have poor performance
- Lisp will sometimes need to reallocate the memory for the hash table when inserting a key
- This will cause an occasional slow key insertion
- Both arrays and hash tables are not considered very lispy
- They are best avoided until performance concerns arise
Structures
defstruct
- The
defstruct
macro is useful for building structured data out of lists - The
defstruct
macro will also create functions for building and accessing the data from the struct
(defstruct rectangle x y width height) (defparameter *my-rect* (make-rectangle :x 10 :y 10 :width 50 :height 25)) (rectangle-height *my-rect*) (setf (rectangle-x *my-rect*) 85) *my-rect*
- The above example shows creating a structure with
defstruct
and building one with themake-<STRUCT_NAME>
function - It also shows using a generated accessor function
<STRUCT_NAME>-<PROPERPTY_NAME>
- The above example produces the following struct literal,
#S(RECTANGLE :X 85 :Y 10 :WIDTH 50 :HEIGHT 25)
Default Values
- It is possible to provide default values to a struct by wrapping the slot with parenthesis and providing a value
(defstruct rectangle (x 10) (y 10) (width 100) (height 100)) (make-rectangle)
- The above example shows defining a struct with default values
- When initializing the struct the slots can be omitted and they will use the default values
- The above example returns the following struct literal,
#S(RECTANGLE :X 10 :Y 10 :WIDTH 100 :HEIGHT 100)
Including Another Structure
- It is possible to declare a struct that includes the slots from another struct
- The syntax for doing this is to wrap the structure name in parenthesis then add a
(:include some-other-struct)
inside
(defstruct point (x 5) (y 5)) (defstruct (circle (:include point)) (radius 4)) (make-circle :x 25 :y 30 :radius 10)
- The above example shows creating a
circle
structure that inherits all the slot from thepoint
struct - This example returns this struct literal,
#S(CIRCLE :X 5 :Y 5 :RADIUS 4)
Streams
- There are two types of streams
input
andoutput
- The predicates
input-stream-p
andoutput-stream-p
will indicate which direction a stream is - The standard
print
andread
functions work with streams - Streams support most of the functions that lists do with the exception of
setf
- A string stream is useful for testing code that operates on streams
(with-output-to-string (*standard-output*) (princ "Hello world!"))
- The above example shows a with form that will collect anything that would normally be sent to standard output and instead return it as a string
Quick Lisp
- Quicklisp is a library manager (package manager) for Common Lisp
Installation
- Download
quicklisp.lisp
form their website,curl -O https://beta.quicklisp.org/quicklisp.lisp
- Download the PGP signature from their website,
curl -O https://beta.quicklisp.org/quicklisp.lisp.asc
- Download the release signing public key,
curl -O https://beta.quicklisp.org/release-key.txt
- Import the release signing key,
gpg --import release-key.txt
- Verify the
quicklisp.lisp
file,gpg --verify quicklisp.lisp.asc quicklisp.lisp
- Start up a Common Lisp environment
- Inside the REPL run,
(load "quicklisp.lisp")
(Ensure the REPL was started in the same folder Quicklisp was downloaded to) - Run
(quicklisp-quickstart:install)
to install Quicklisp - This will create a
quicklisp
folder in your home directory - Normally when starting a new lisp session you would run
(load "~/quicklisp/setup.lisp)
to load Quicklisp into the running session - Use
(ql:add-to-init-file)
to have this happen automatically - Install the
slime helper
,(ql:quickload "quicklisp-slime-helper")
- Be sure to follow the instructions
Installing Libraries
- This is an example of installing a package,
(ql:quickload "vecto")
- This is an example of uninstalling a package,
(ql:uninstall "vecto")
- To search for a library to install use apropos,
(ql:system-apropos "sdl2")
Upgrading Quicklisp
- Use
(ql:update-dist "quicklisp")
to update Quicklisp - Run
(ql:update-client)
to update the client
Local Projects
- To use the Common Lisp systems create symlinks to the
~/quicklisp/local-projects
folder for folders underneath thelisp/
directory - Once the symlinks are loaded inside the REPL run
(ql:register-local-projects)
for quicklisp to know about the system - You should be able to use
(ql:quickload "my-local-project")
to load your system
Debugging
- malisper: Debugging Lisp Part 1: Recompilation
- When using SBCL you need to adjust the optimization flags to enable debugging
(declaim (optimize (debug 3)))
- To step through a function use the
step
higher order function
(step (my-fun '(a b c)))
- You might need to recompile the code you are inspecting after setting the debug level
Examples
Split String into List of Words
(defun split-by-one-space (string) "Returns a list of substrings of string divided by ONE space each. Note: Two consecutive spaces will be seen as if there were an empty string between them." (loop for i = 0 then (1+ j) as j = (position #\Space string :start i) collect (subseq string i j) while j))
- Alternatively use
cl-ppcre
,(cl-ppcre:split #\Space some-string)